package editors.objectedit;
import java.io.File;
import java.util.Vector;

import game.engine.GSync;
import game.engine.GSync.SyncType;
import game.objects.GODynamic;
import game.objects.GameObject;
import graphics.objects.GraphicsObject;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import main.Config;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import physics.Body;
import physics.joints.Joint;
import physics.joints.TwoBodyJoint;
import physics.shapes.Circle;
import physics.shapes.Polygon;
import physics.shapes.Shape;
import tools.Help;
import tools.Log;
import tools.Point;
public class SaveObject {
	private static Vector<Body> selectedBodies;
	private static Vector<GraphicsObject> selectedGROs;
	public static Point center;
	public static void save(String fileName, GODynamic go) {
		fileName = Config.MODELFOLDER + fileName;
		BoundingBox box = new BoundingBox(go);
		Document doc = createDocument();
		Element gameObjectElement = createGameObjectElement(doc);
		writeBoundingBox(gameObjectElement, box);
		writeGameObject(doc, gameObjectElement, go);
		writeFile(fileName, doc);
	}
	public static Document saveCopy(GODynamic go, Vector<Body> bodies, Vector<GraphicsObject> gros) {
		selectedBodies = (Vector<Body>) bodies.clone();
		selectedGROs = (Vector<GraphicsObject>) gros.clone();
		BoundingBox box = new BoundingBox(bodies, gros);
		center = box.center;
		Document doc = createDocument();
		Element gameObjectElement = createGameObjectElement(doc);
		writeBoundingBox(gameObjectElement, box);
		writeGameObject(doc, gameObjectElement, go);
		selectedBodies = null;
		selectedGROs = null;
		return doc;
	}
	private static void writeBoundingBox(Element el, BoundingBox box) {
		el.setAttribute("box", box.minX + " " + box.minY + " " + box.maxX + " " + box.maxY);
	}
	private static void writeFile(String fileName, Document doc) {
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		Transformer transformer;
		try {
			transformer = transformerFactory.newTransformer();
			// transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //would format the xml file but loading won't work .. dont know y!!!
			DOMSource source = new DOMSource(doc);
			StreamResult result = new StreamResult(new File(fileName));
			transformer.transform(source, result);
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}
	}
	private static Document createDocument() {
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder;
		try {
			docBuilder = docFactory.newDocumentBuilder();
			Document doc = docBuilder.newDocument();
			return doc;
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
		return null;
	}
	private static Element createGameObjectElement(Document doc) {
		Element root = doc.createElement("GameObject");
		doc.appendChild(root);
		return root;
	}
	// Write Bodies, Joints and GraphicsObjects and Synchronization between them
	private static void writeGameObject(Document doc, Element gameObjectElement, GODynamic go) {
		for (int i = 0; i < go.numBodies; i++) {
			Body b = go.getBody(i);
			writeBody(doc, gameObjectElement, b, go);
		}
		for (int i = 0; i < go.numJoints; i++) {
			Joint j = go.getJoint(i);
			writeJoint(doc, gameObjectElement, j, go);
		}
		for (int i = 0; i < go.numGraphicsObjects; i++) {
			GraphicsObject gro = go.getGraphicsObject(i);
			writeGraphicsObject(doc, gameObjectElement, go, gro);
		}
	}
	private static void writeBody(Document doc, Element gameObjectElement, Body b, GameObject go) {
		if (selectedBodies != null && !selectedBodies.contains(b))
			return;
		Element be = doc.createElement("body");
		gameObjectElement.appendChild(be);
		// Write position, rotation, friction, restitution, mass and the bodies
		// shape.
		be.setAttribute("pos", b.pos.toFileString());
		be.setAttribute("apos", String.valueOf(b.apos));
		be.setAttribute("friction", String.valueOf(b.friction));
		be.setAttribute("restitution", String.valueOf(b.restitution));
		be.setAttribute("mass", String.valueOf(b.mass));
		be.setAttribute("id", String.valueOf(b.id));
		be.setAttribute("name", go.getName(b));
		String c = "";
		for (int i = 0; i < b.nonCollidingBodies.size(); i++) {
			c = c + b.nonCollidingBodies.get(i) + ";";
		}
		be.setAttribute("noncolbodies", c);
		writeShape(doc, be, b.shape);
	}
	private static void writeShape(Document doc, Element be, Shape shape) {
		if (shape instanceof Polygon) {
			Polygon poly = (Polygon) shape;
			Element se = doc.createElement("polygon");
			be.appendChild(se);
			se.setAttribute("pos", poly.pos.toFileString());
			for (Point p : poly.points) {
				Element pe = doc.createElement("point");
				se.appendChild(pe);
				pe.setAttribute("xy", p.toFileString());
			}
			return;
		}
		else if (shape instanceof Circle) {
			Circle circle = (Circle) shape;
			Element se = doc.createElement("circle");
			be.appendChild(se);
			se.setAttribute("pos", circle.pos.toFileString());
			se.setAttribute("radius", String.valueOf(circle.radius));
			return;
		}
		assert (false);
	}
	private static void writeJoint(Document doc, Element gameObjectElement, Joint j, GameObject go) {
		if (selectedBodies != null) {
			boolean r = true;
			if (selectedBodies.contains(j.b1))
				r = false;
			if (j instanceof TwoBodyJoint) {
				if (!r && selectedBodies.contains(((TwoBodyJoint) j).b2))
					r = false;
			}
			if (r)
				return;
		}
		Element je = doc.createElement(j.getClass().getSimpleName());
		gameObjectElement.appendChild(je);
		je.setAttribute("data", j.toFileString());
		je.setAttribute("name", go.getName(j));
	}
	private static void writeGraphicsObject(Document doc, Element gameObjectElement, GameObject go, GraphicsObject gro) {
		if (selectedGROs != null && !selectedGROs.contains(gro)) {
			return;
		}
		Element je = doc.createElement(gro.getClass().getSimpleName());
		gameObjectElement.appendChild(je);
		je.setAttribute("data", gro.toFileString());
		je.setAttribute("name", go.getName(gro));
		writeSync(je, go, gro);
	}
	private static void writeSync(Element je, GameObject go, GraphicsObject gro) {
		GSync s = go.getSync(gro);
		if (selectedBodies != null && s.type == SyncType.BODY) {
			boolean f = false;
			for (Body b : selectedBodies) {
				if (b.id == s.targetId) {
					f = true;
					break;
				}
			}
			if (!f)
				s = new GSync();
		}
		je.setAttribute("type", s.type.toString());
		je.setAttribute("targetId", String.valueOf(s.targetId));
		je.setAttribute("groId", String.valueOf(s.grOid));
		je.setAttribute("offset", s.offset.toFileString());
		je.setAttribute("angleoffset", String.valueOf(s.angleoffset));
	}
}
